using System;
using System.Reflection;
using Moq;
using uMVP;
using uMVP.AutofacExtends;
using UnityEngine;

namespace Autofac.Extras.Moq
{
    public partial class AutoMock
    {
        public static AutoMock GetLoose<TViewInterface, TViewClass, TPresenterInterface>(Action<ContainerBuilder> beforeBuild = null)
            where TViewClass : ViewBase
            where TPresenterInterface : class, IPresenter
        {
            Canvas canvas = null;
            return GetLoose(
                builder =>
                {
                    beforeBuild?.Invoke(builder);
                    var mockPresenter = new Mock<TPresenterInterface>();
                    var presenter = mockPresenter.Object;
                    builder.RegisterInstance(presenter).As<TPresenterInterface>();
                    builder.RegisterType<TViewClass>()
                        .OnActivated(e =>
                        {
                            try
                            {
                                var view = (IView<TPresenterInterface>)e.Instance;
                                canvas = CreateCanvas(view);
                                ContainerBuilderExtends.InitializePrefabInstance(view);
                                view.InitializeAsync(presenter);
                                presenter.InitializeAsync();
                            }
                            catch (Exception exception)
                            {
                                throw new Exception(exception.ToString());
                            }
                        })
                        .OnRelease(view =>
                        {
                            if (canvas != null)
                                UnityEngine.Object.DestroyImmediate(canvas);
                        })
                        .As<TViewClass>()
                        .As<TViewInterface>();
                });
        }
        
        public static AutoMock GetLoose<TViewInterface, TViewClass, TPresenterInterface, TPresenterClass>(Action<ContainerBuilder> beforeBuild = null)
            where TViewInterface : class, IView
            where TViewClass : ViewBase
            where TPresenterInterface : class, IPresenter
        {
            return GetLoose(
                builder =>
                {
                    beforeBuild?.Invoke(builder);
                    var view = new Mock<TViewInterface>().Object;
                    builder.RegisterInstance(view).As<TViewInterface>();
                    builder.RegisterType<TPresenterClass>().As<TPresenterClass>().As<TPresenterInterface>();
                });
        }
        
        private static Canvas CreateCanvas(object view)
        {
            MemberInfo info = view.GetType();
            var attributes = info.GetCustomAttributes(typeof(PrefabPathAttribute), true);
            foreach (var attribute in attributes)
            {
                if (attribute.GetType() == typeof(PrefabPathAttribute))
                {
                    var prefabPathAttribute = (PrefabPathAttribute)attribute;
                    var path = prefabPathAttribute.ParentPath;
                    var canvasObject = GameObject.Find(path);
                    if (canvasObject != null)
                        UnityEngine.Object.DestroyImmediate(canvasObject);

                    return CreateCanvas(path);
                }
            }

            return null;
        }

        private static Canvas CreateCanvas(string path)
        {
            var nameList = path.Split('/');

            GameObject go = null;
            foreach (var name in nameList)
            {
                if (string.IsNullOrEmpty(name))
                    continue;
                
                var gameObject = new GameObject(name);
                if (go != null)
                    gameObject.transform.SetParent(go.transform);
                go = gameObject;
            }
            
            return go.AddComponent<Canvas>();
        }
    }
}